#pragma once

#include <string>
#include <strings.h>
#include <functional>

/*网络必要的头文件*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*日志头文件*/
#include "logMessage.hpp"

#include <unistd.h>
#include <sys/wait.h>

#include "singletonPthreadPool.hpp"
#include "Task.hpp"

namespace server
{
    using namespace std;
    using namespace threadpool;
    class tcpServer;

    struct threadData
    {
        tcpServer *_this;
        int _sock;
    };
    enum
    {
        SCOKET_ERROR = 1,
        BIND_ERROR,
        LISTEN_ERROR,
        START_ERROR,
        FATAL_ERROR
    };
    static const int gBacklog = 5;
    class tcpServer
    {
    public:
        tcpServer(const uint16_t &port)
            : _socketFd(-1), _port(port)
        {
            /*以字节流的方式打开网络文件，即TCP协议*/
            _socketFd = socket(AF_INET, SOCK_STREAM, 0);
            if (_socketFd == -1)
            {
                logMessage(FATAL, "socket failed");
                exit(SCOKET_ERROR);
            }
            logMessage(NORMAL, "socket success,socketFd[%d]",_socketFd);

            struct sockaddr_in server = getSockaddr_in();

            /*绑定套接字*/
            int n = bind(_socketFd, (struct sockaddr *)&server, sizeof(server));
            if (n == -1)
            {
                logMessage(FATAL, ":bind failed");
                exit(BIND_ERROR);
            }
            logMessage(NORMAL, "bind success,return value of bind[%d]",n);

            /*开始监听*/
            n = listen(_socketFd, gBacklog);
            if (n == -1)
            {
                logMessage(FATAL, "listen failed");
                exit(LISTEN_ERROR);
            }
            logMessage(NORMAL, "listen success,return value of listen[%d]",n);
        }

        void start()
        {
            while (true)
            {
                struct sockaddr_in client = getSockaddr_in();
                socklen_t len = sizeof(client);
                int sock = accept(_socketFd, (struct sockaddr *)&client, &len);
                if (sock == -1)
                { /*如果accept失败，说明当前连接失败，不会影响其他连接*/
                    logMessage(ERROR, "accept failed,next...");
                    continue;
                }
                logMessage(NORMAL, "accept success,get a new sock[%d]",sock);

                // processVersion(sock);/*多进程版本*/

                //threadVersion(sock); /*多线程版本*/

                threadPoolVersion(sock);/*线程池版本*/
            }
        }

    private:
        void threadPoolVersion(int sock)
        {
            Task task(sock,serviceTask);
            threadPool<Task>::getInstance()->start();
            threadPool<Task>::getInstance()->push(task);
        }



        static void *serviceThread(void *args)
        {
            threadData *pd = static_cast<threadData *>(args);
            pd->_this->serviceTask(pd->_sock);
            close(pd->_sock);
            delete pd;
            return nullptr;
        }

        void threadVersion(int sock)
        {
            pthread_t tid;
            threadData *pd = new threadData();
            pd->_sock = sock;
            pd->_this = this;
            pthread_create(&tid, nullptr, serviceThread, pd);
        }

        void processVersion(int sock)
        {
            /*多进程版，当然可以使用signal函数忽略进程的退出信号
             *这样就不用创建"孙子进程"了
             *但是我们不那样做*/
            int id = fork();
            if (id == 0)
            { /*子进程天生继承父进程的文件描述符
               *但是要关掉多余的描述符，例如用于监听的文件描述符*/
                close(_socketFd);

                /*当前进程再创建一个子进程，创建之后当前进程退出
                 *当前进程的父进程在外部直接等待当前进程成功*/
                if (fork() > 0)
                    exit(0);

                /*当前进程创建的进程去执行业务逻辑
                 *这个新创建的进程退出之前，其父进程就退出了，所以它就是孤儿进程
                 *它的养父就是操作系统，不需要我们关心等待问题*/
                serviceTask(sock);
                close(sock);
                exit(0);
            }
            pid_t ret = waitpid(id, nullptr, 0);
            if (ret > 0)
                cout << "wait child process succuss: " << ret << endl;
        }

        static void serviceTask(int sock)
        {
            while (true)
            {
                char buffer[1024];
                int n = read(sock, buffer, sizeof(buffer) - 1);
                if (n > 0)
                {
                    buffer[n] = 0;
                    cout << buffer << endl;
                    string str = buffer;
                    str += " server[echo]";
                    write(sock, str.c_str(), str.size());
                }
                else
                { /*客户端退出*/
                    logMessage(NORMAL,"Clinet quit,quit sock[%d]",sock);
                    break;
                }
            }
        }

    private:
        /*服务端只关心端口号*/
        int _socketFd;
        uint16_t _port;

        /*将用于网络通信的结构体的设置封装成模块
         *由于编译器优化实际上只发生一次拷贝构造*/
        struct sockaddr_in getSockaddr_in()
        {
            struct sockaddr_in server;
            bzero((void *)&server, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_port);
            server.sin_addr.s_addr = INADDR_ANY;
            return server;
        }
    };
} /*namespace server ends here*/